home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / misc / emu / msh-156.lha / han / hanmain.c < prev    next >
C/C++ Source or Header  |  1996-12-22  |  16KB  |  672 lines

  1. /*-
  2.  * $Id: hanmain.c,v 1.56 1996/12/22 00:22:33 Rhialto Rel $
  3.  * $Log: hanmain.c,v $
  4.  * Revision 1.56  1996/12/22  00:22:33  Rhialto
  5.  * Add IEQUALIFIER_MULTIBROADCAST to disk inserted/removed events.
  6.  *
  7.  * Revision 1.55  1993/12/30  23:02:45    Rhialto
  8.  * New LONGNAMES filesystem, changes throughout the handler.
  9.  * Optional (compile-time) broadcast IECLASS_DISKINSERTED messages.
  10.  * Don't fail Info() if there is no disk in drive.
  11.  * Freeze for MAXON5.
  12.  *
  13.  * Revision 1.54  1993/06/24  05:12:49    Rhialto
  14.  * DICE 2.07.54R.
  15.  *
  16.  * Revision 1.53  92/10/25  02:25:46  Rhialto
  17.  * Expose private info if user asks nicely.
  18.  *
  19.  * Revision 1.52  92/09/06  00:19:31  Rhialto
  20.  * Include $VER in version string.
  21.  *
  22.  * Revision 1.51  92/04/17  15:36:35  Rhialto
  23.  * Freeze for MAXON. removed InitCacheList() from MSDiskInserted().
  24.  *
  25.  * Revision 1.46  91/10/06  18:27:36  Rhialto
  26.  *
  27.  * Freeze for MAXON
  28.  *
  29.  * Revision 1.45  91/10/03  23:36:47  Rhialto
  30.  * Implement conversions during Read()/Write()
  31.  *
  32.  * Revision 1.43  91/09/28  01:40:24  Rhialto
  33.  * Changed to newer syslog stuff.
  34.  *
  35.  * Revision 1.42  91/06/13  23:50:21  Rhialto
  36.  * DICE conversion
  37.  *
  38.  * Revision 1.40  91/03/03  17:46:39  Rhialto
  39.  * Freeze for MAXON
  40.  *
  41.  * Revision 1.32  90/11/23  23:54:07  Rhialto
  42.  * Prepare for syslog
  43.  *
  44.  * Revision 1.31  90/11/10  02:46:35  Rhialto
  45.  * Patch 3a. Changes location of disk volume date.
  46.  *
  47.  * Revision 1.30  90/06/04  23:16:50  Rhialto
  48.  * Release 1 Patch 3
  49.  *
  50.  *  HANMAIN.C
  51.  *
  52.  *  The code for the messydos file system handler.
  53.  *
  54.  *  Some start/stop stuff that is not really part of the
  55.  *  file system itself but that must be done anyway.
  56.  *
  57.  *  This code is (C) Copyright 1989-1994 by Olaf Seibert. All rights reserved.
  58.  *  May not be used or copied without a licence.
  59. -*/
  60.  
  61. #include "han.h"
  62. #include "dos.h"
  63. #if CONVERSIONS
  64. #   include "hanconv.h"
  65. #endif
  66. #if INPUTDEV
  67. #include <devices/input.h>
  68. #include <devices/inputevent.h>
  69. #include <clib/intuition_protos.h>
  70. #endif
  71.  
  72. #include <string.h>
  73. #if HDEBUG
  74. #   include "syslog.h"
  75. #else
  76. #   define    debug(x)
  77. #endif
  78.  
  79. Prototype byte ToUpper(byte ch);
  80. Prototype long lmin(long a, long b);
  81. Prototype byte *ZapSpaces(byte *begin, byte *end);
  82. Prototype byte *ToMSName(byte *dest, byte *source);
  83. Prototype long MSDiskInfo(struct InfoData *infodata);
  84. Prototype void MSDiskInserted(struct LockList **locks, void *cookie);
  85. Prototype int MSDiskRemoved(struct LockList **locks);
  86. Prototype void InputDiskInserted(void);
  87. Prototype void InputDiskRemoved(void);
  88. Prototype void HanCloseDown(void);
  89. Prototype int HanOpenUp(void);
  90. Prototype long MSRelabel(byte *newname);
  91. Prototype struct PrivateInfo *PrivateInfo(void);
  92.  
  93. struct Library *IntuitionBase;
  94. #if INPUTDEV
  95. struct IOStdReq *InputIOReq;
  96. #endif
  97. Local const char RCSId[] = "\0$\VER: Messydos filing system $Revision: 1.56 $ $Date: 1996/12/22 00:22:33 $, by Olaf Seibert";
  98.  
  99. #define CONV_SEP    ';'
  100.  
  101. byte
  102. ToUpper(ch)
  103. byte        ch;
  104. {
  105.     if (ch >= 'a' && ch <= 'z')
  106.     return ch + ('A' - 'a');
  107.     if (ch == '.')
  108.     return '!';
  109.     return ch & ~DIR_DELETED_MASK;
  110. }
  111.  
  112. long
  113. lmin(a, b)
  114. long        a,
  115.         b;
  116. {
  117.     return (a < b) ? a : b;
  118. }
  119.  
  120. byte           *
  121. ZapSpaces(begin, end)
  122. byte           *begin,
  123.            *end;
  124. {
  125.     *end = '\0';                /* Make sure the string is 0-terminated */
  126.  
  127.     while (end > begin && end[-1] == ' ')
  128.     *--end = '\0';
  129.  
  130.     return end;
  131. }
  132.  
  133. /*
  134.  * Map an arbitrary file name to MS-DOS conventions. The output format is
  135.  * 8+3 without dot, padded with spaces, suitable for direct comparison
  136.  * with directory entries. Return a pointer to the delimiter found ('\0'
  137.  * or '/'). [[Make sure that Examine/ExNext return a proper inverse of
  138.  * this...]]
  139.  */
  140.  
  141. byte           *
  142. ToMSName(dest, source)
  143. byte           *dest;
  144. byte           *source;
  145. {
  146. #if LONGNAMES == 0
  147.     byte       *dotp;
  148.     byte       *slashp;
  149.     int         i;
  150. #endif
  151.     int         len;
  152.  
  153.     if (*source == '/') {       /* parentdir */
  154.     strncpy(dest, DotDot, L_8 + L_3);   /* ".." */
  155.     return source;
  156.     }
  157. #if LONGNAMES
  158.     /* Pad with nuls instead of spaces */
  159.     memset(dest, 0, L_8 + L_3);
  160.  
  161.     for (len = 0;
  162.         *source && *source != '/' && *source != CONV_SEP && len < L_8;
  163.         len++)
  164.     *dest++ = *source++;
  165.  
  166.     if (source[0] == CONV_SEP && source[1]) {
  167.     int        c;
  168.  
  169.     source++;
  170. #if CONVERSIONS
  171.     c = source[0] & 31;
  172.     if (c >= ConvFence)
  173.         c = ConvNone;
  174.     ConversionImbeddedInFileName = c;
  175. #endif /* CONVERSIONS */
  176.  
  177.     while (*source && *source != '/')
  178.         source++;
  179.     }
  180.     return source;
  181. #else
  182.     /*
  183.      * Remove any strictly leading dots. .info -> info, .indent.pro ->
  184.      * indent.pro, .profile -> profile, etc.
  185.      */
  186.     while (*source == '.')
  187.     source++;
  188.  
  189.     /*
  190.      * Find dot and slash which are delimiters of name and extension.
  191.      */
  192.     {
  193.     byte           *cp;
  194.  
  195.     cp = source;
  196.     while (*cp) {
  197.         if (*cp == '.' || *cp == '/' || *cp == CONV_SEP)
  198.         break;
  199.         cp++;
  200.     }
  201.     dotp = cp;
  202.     while (*cp) {
  203.         if (*cp == '/' || *cp == CONV_SEP)
  204.         break;
  205.         cp++;
  206.     }
  207.     slashp = cp;
  208.     }
  209.  
  210.     len = dotp - source;
  211.     if (len > L_8)
  212.     len = L_8;
  213.  
  214.     for (i = 0; i < len; i++) {
  215.     *dest++ = ToUpper(*source++);
  216.     }
  217.     for (; i < L_8; i++) {
  218.     *dest++ = ' ';
  219.     }
  220.  
  221.     source = dotp + 1;
  222.     len = slashp - source;    /* so will be -1 if no suffix */
  223.     if (len > 3)
  224.     len = 3;
  225.  
  226.     for (i = 0; i < len; i++) {
  227.     *dest++ = ToUpper(*source++);
  228.     }
  229.     for (; i < 3; i++) {
  230.     *dest++ = ' ';
  231.     }
  232.  
  233.     if (slashp[0] == CONV_SEP && slashp[1]) {
  234.     int        c;
  235.  
  236.     slashp++;
  237. #if CONVERSIONS
  238.     c = slashp[0] & 31;
  239.     if (c >= ConvFence)
  240.         c = ConvNone;
  241.     ConversionImbeddedInFileName = c;
  242. #endif /* CONVERSIONS */
  243.  
  244.     while (*slashp && *slashp != '/')
  245.         slashp++;
  246.     }
  247.     return slashp;
  248. #endif
  249. }
  250.  
  251. /*
  252.  * Do the Info call.
  253.  */
  254.  
  255. long
  256. MSDiskInfo(infodata)
  257. struct InfoData *infodata;
  258. {
  259.     setmem(infodata, sizeof(*infodata), 0);
  260.  
  261.     infodata->id_DiskState = IDDiskState;
  262.     infodata->id_DiskType = IDDiskType;
  263.     infodata->id_UnitNumber = UnitNr;
  264.     debug(("DiskInfo: state %d, type %x\n", IDDiskState, IDDiskType));
  265.  
  266.     infodata->id_VolumeNode = (BPTR) CTOB(VolNode);
  267.     infodata->id_InUse = LockList ? 1 : 0;
  268.  
  269.     /*if (IDDiskType == ID_DOS_DISK)*/ {
  270.     infodata->id_NumBlocks = Disk.nsects;
  271.     infodata->id_NumBlocksUsed = Disk.nsects - Disk.freeclusts * Disk.spc;
  272.     infodata->id_BytesPerBlock = Disk.bps;
  273.     }
  274.  
  275.     return DOSTRUE;
  276. }
  277.  
  278. /*
  279.  * We (re-)establish our List of MSFileLocks after a disk has been
  280.  * (re-)inserted. If there are no known locks, we make the root lock from
  281.  * the volume label, if there is one.
  282.  *
  283.  * We get a special cookie to hand to a cleanup routine that we must call
  284.  * when finally all locks on the current disk are UnLock()ed. (this is
  285.  * actually the volume node, but we don't want to know that.)
  286.  *
  287.  * This must be called some time after IdentifyDisk().
  288.  */
  289.  
  290. void
  291. MSDiskInserted(locks, cookie)
  292. struct LockList **locks;
  293. void           *cookie;
  294. {
  295.     debug(("MSDiskInserted %08lx\n", cookie));
  296.  
  297.     LockList = *locks;
  298.  
  299.     if (LockList == NULL) {
  300.     LockList = NewLockList(cookie);
  301.     RootLock = MakeLock(NULL, &Disk.vollabel, SHARED_LOCK);
  302.     } else {
  303.     RootLock = MSDupLock(GetTail(&LockList->ll_List));
  304.     }
  305. }
  306.  
  307. /*
  308.  * Remove the current disk. A place is offered to save the current
  309.  * LockList to restore later. We must unlock the root lock since it isn't
  310.  * a real reference to the disk, just a placeholder for dummies that hand
  311.  * us NULL locks.
  312.  */
  313.  
  314. int
  315. MSDiskRemoved(locks)
  316. struct LockList **locks;
  317. {
  318.     debug(("MSDiskRemoved(), FatDirty=%d, CacheDirty=%d\n",
  319.         FatDirty, CacheDirty));
  320. #if !READONLY
  321.     if (FatDirty || CacheDirty) {
  322.     MSUpdate(1);        /* Force a requester */
  323.     }
  324. #endif
  325.  
  326.     FreeFat();
  327.     FreeCacheList();
  328.  
  329.     IDDiskType = ID_NO_DISK_PRESENT;
  330.     IDDiskState = ID_WRITE_PROTECTED;
  331.     *locks = NULL;
  332.  
  333.     if (RootLock == NULL) {
  334.     debug(("MSDiskRemoved with no RootLock\n"));
  335.     return 1;
  336.     }
  337. #if HDEBUG
  338.     if (RootLock != GetTail(&LockList->ll_List)) {
  339.     debug(("RootLock not at end of LockList!\n"));
  340.     /* Get the lock on the root dir at the tail of the List */
  341.     Remove((struct Node *)RootLock);
  342.     AddTail((struct List *)&LockList->ll_List, (struct Node *)RootLock);
  343.     }
  344. #endif
  345.  
  346.     /*
  347.      * If there are no real locks on the disk, we need not keep any
  348.      * information about it.
  349.      */
  350.  
  351.     MSUnLock(RootLock);     /* may call FreeLockList and free VolNode
  352.                  * (!) */
  353.     RootLock = NULL;
  354.  
  355.     if (LockList) {
  356.     *locks = LockList;    /* VolNode can't be gone now... */
  357.     LockList = NULL;
  358.     return 0;        /* not all references gone */
  359.     } else {
  360.     return 1;        /* all gone, even the VolNode */
  361.     }
  362. }
  363.  
  364. #if INPUTDEV
  365. void
  366. InputDiskInserted(void)
  367. {
  368.     struct InputEvent ie;
  369.  
  370.     memset(&ie, 0, sizeof(ie));
  371.     ie.ie_Class = IECLASS_DISKINSERTED;
  372.     ie.ie_Qualifier = IEQUALIFIER_MULTIBROADCAST;
  373.     /*
  374.      * Use Intuition to get the current time.
  375.      * If we were a 2.04+ only application, we could use
  376.      * timer.device/GetSysTime().
  377.      */
  378.     CurrentTime(&ie.ie_TimeStamp.tv_secs, &ie.ie_TimeStamp.tv_micro);
  379.  
  380.     InputIOReq->io_Command = IND_WRITEEVENT;
  381.     InputIOReq->io_Data = &ie;
  382.     InputIOReq->io_Length = sizeof(ie);
  383.     DoIO((struct IORequest *)InputIOReq);
  384.     debug(("IECLASS_DISKINSERTED %d\n", InputIOReq->io_Error));
  385. }
  386.  
  387. void
  388. InputDiskRemoved(void)
  389. {
  390.     struct InputEvent ie;
  391.  
  392.     memset(&ie, 0, sizeof(ie));
  393.     ie.ie_Class = IECLASS_DISKREMOVED;
  394.     ie.ie_Qualifier = IEQUALIFIER_MULTIBROADCAST;
  395.     /* Use Intuition to get the current time */
  396.     CurrentTime(&ie.ie_TimeStamp.tv_secs, &ie.ie_TimeStamp.tv_micro);
  397.  
  398.     InputIOReq->io_Command = IND_WRITEEVENT;
  399.     InputIOReq->io_Data = &ie;
  400.     InputIOReq->io_Length = sizeof(ie);
  401.     DoIO((struct IORequest *)InputIOReq);
  402.     debug(("IECLASS_DISKREMOVED %d\n", InputIOReq->io_Error));
  403. }
  404. #endif
  405.  
  406. void
  407. HanCloseDown()
  408. {
  409. #if HDEBUG
  410.     struct MSFileLock *fl;
  411.  
  412.     while (LockList && (fl = (struct MSFileLock *) GetHead(&LockList->ll_List))) {
  413.     debug(("UNLOCKING %08lx: ", fl));
  414.     PrintDirEntry((struct DirEntry *)&fl->msfl_Msd);
  415.     MSUnLock(fl);        /* Remove()s it from this List */
  416.     }
  417. #endif
  418. #if CONVERSIONS
  419.     ConvCleanUp();
  420. #endif
  421. #if INPUTDEV
  422.  
  423.     if (InputIOReq) {
  424.     if (InputIOReq->io_Unit) {
  425.         CloseDevice((struct IORequest *)InputIOReq);
  426.     }
  427.     DeleteExtIO((struct IORequest *)InputIOReq);
  428.     InputIOReq = NULL;
  429.     }
  430. #endif
  431.     if (DiskIOReq) {
  432.     if (DiskIOReq->iotd_Req.io_Unit) {
  433.         MSUpdate(1);
  434.         CloseDevice((struct IORequest *)DiskIOReq);
  435.     }
  436.     DeleteExtIO((struct IORequest *)DiskIOReq);
  437.     DiskIOReq = NULL;
  438.     }
  439.     if (TimeIOReq) {
  440.     if (TimeIOReq->tr_node.io_Unit) {
  441.         WaitIO((struct IORequest *)TimeIOReq);
  442.         CloseDevice((struct IORequest *)TimeIOReq);
  443.     }
  444.     DeleteExtIO((struct IORequest *)TimeIOReq);
  445.     TimeIOReq = NULL;
  446.     }
  447.     if (DiskReplyPort) {
  448.     DeletePort(DiskReplyPort);
  449.     DiskReplyPort = NULL;
  450.     }
  451.     if (IntuitionBase) {
  452.     CloseLibrary(IntuitionBase);
  453.     IntuitionBase = NULL;
  454.     }
  455. }
  456.  
  457. int
  458. HanOpenUp()
  459. {
  460.     LockList = NULL;
  461.     RootLock = NULL;
  462.     Fat = NULL;
  463.     IDDiskType = ID_NO_DISK_PRESENT;
  464.     IDDiskState = ID_WRITE_PROTECTED;
  465.     DelayCount = 0;
  466.     Disk.bps = MS_BPS;
  467.     CheckBootBlock = CHECK_BOOTJMP | CHECK_SANITY | CHECK_SAN_DEFAULT;
  468.     InitCacheList();
  469.  
  470.     TimeIOReq = NULL;
  471.  
  472. #if HDEBUG
  473.     if (!(DiskReplyPort = CreatePort("MSH:disk.replyport", -1L)))
  474.     goto abort;
  475. #else
  476.     if (!(DiskReplyPort = CreatePort(NULL, 0L)))
  477.     goto abort;
  478. #endif
  479.  
  480.     debug(("DiskReplyPort = 0x%08lx\n", DiskReplyPort));
  481.  
  482.     if (!(DiskIOReq = (struct IOExtTD *)
  483.               CreateExtIO(DiskReplyPort, (long) sizeof(*DiskIOReq)))) {
  484.     debug(("Failed to CreateExtIO\n"));
  485.     goto abort;
  486.     }
  487.     if (OpenDevice(DevName, UnitNr, (struct IORequest *)DiskIOReq,
  488.            DevFlags | TDF_ALLOW_NON_3_5)) {
  489.     debug(("Failed to OpenDevice\n"));
  490.     goto abort;
  491.     }
  492.     TimeIOReq = (struct timerequest *) CreateExtIO(DiskReplyPort,
  493.                          (long) sizeof(*TimeIOReq));
  494.  
  495.     if (TimeIOReq == NULL || OpenDevice(TIMERNAME, UNIT_VBLANK,
  496.                     (struct IORequest *)TimeIOReq, 0L))
  497.     goto abort;
  498.     TimeIOReq->tr_node.io_Flags = IOF_QUICK;    /* For the first WaitIO() */
  499.  
  500. #if INPUTDEV
  501.     if (!(InputIOReq = (struct IOStdReq *)CreateExtIO(DiskReplyPort, (long) sizeof(*InputIOReq)))) {
  502.     debug(("Failed to CreateExtIO for input.device\n"));
  503.     goto abort;
  504.     }
  505.     if (OpenDevice("input.device", 0, (struct IORequest *)InputIOReq, 0)) {
  506.     debug(("Failed to Open input.device\n"));
  507.     goto abort;
  508.     }
  509. #endif
  510.  
  511.     IntuitionBase = OpenLibrary("intuition.library", 0L);
  512.     debug(("HanOpenUp() done.\n"));
  513.     return DOSTRUE;
  514.  
  515. abort:
  516.     HanCloseDown();
  517.     return 0;
  518. }
  519.  
  520. /*
  521.  * Relabel the disk. We create new labels if necessary.
  522.  */
  523.  
  524. long
  525. MSRelabel(newname)
  526. byte           *newname;
  527. {
  528. #if READONLY
  529.     return DOSFALSE;
  530. #else
  531.     /*
  532.      * A null or empty string means: remove the label, if any.
  533.      */
  534.     if (!newname || !*newname) {
  535.     if (RootLock->msfl_DirSector != (word)ROOT_SEC) {
  536.         RootLock->msfl_Msd.msd_Name[0] = DIR_DELETED;
  537.         RootLock->msfl_Msd.msd_Attributes = 0;
  538.         WriteFileLock(RootLock);
  539.         Disk.vollabel = FakeRootDirEntry;
  540.         RootLock->msfl_Msd = Disk.vollabel.de_Msd;
  541.         RootLock->msfl_DirSector = Disk.vollabel.de_Sector;
  542.         RootLock->msfl_DirOffset = Disk.vollabel.de_Offset;
  543.     }
  544.     return DOSTRUE;
  545.     }
  546.  
  547.     /*
  548.      * No label yet? Then we must create one, even if we need to move
  549.      * something else for it.
  550.      */
  551.  
  552.     if (RootLock->msfl_DirSector == (word)ROOT_SEC) {
  553.     struct MSFileLock *new;
  554.  
  555.     new = MSLock(RootLock, "OLAF-><>.\\*", EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  556.     if ((new == NULL) && (new = EmptyFileLock)) {
  557.         struct DateStamp dateStamp;
  558.  
  559.         error = 0;
  560.  
  561.         DateStamp(&dateStamp);
  562.         ToMSDate(&msd_CreationDate(Disk.vollabel.de_Msd),
  563.              &msd_CreationTime(Disk.vollabel.de_Msd), &dateStamp);
  564.  
  565.         Disk.vollabel.de_Msd.msd_Date =
  566.         msd_CreationDate(Disk.vollabel.de_Msd);
  567.         Disk.vollabel.de_Msd.msd_Time =
  568.         msd_CreationTime(Disk.vollabel.de_Msd);
  569.  
  570.         if (new->msfl_DirSector == Disk.rootdir) {
  571.         RootLock->msfl_DirSector = Disk.rootdir;
  572.         RootLock->msfl_DirOffset = new->msfl_DirOffset;
  573.         } else {
  574.         /*
  575.          * Move something out of the first directory block. Try
  576.          * not to move system files or directories (. ..), but
  577.          * we'll do it if we need to. Set the root dir date to
  578.          * now.
  579.          */
  580.         byte           *fromsec;
  581.         byte           *tosec;
  582.         struct MsDirEntry *dir;
  583.  
  584.         fromsec = ReadSec(Disk.rootdir);
  585.         tosec = ReadSec(new->msfl_DirSector);
  586.  
  587.         dir = (struct MsDirEntry *) fromsec;
  588.         while (dir->msd_Attributes & (ATTR_SYSTEM | ATTR_DIRECTORY)) {
  589.             if ((byte *) ++dir >= fromsec + Disk.bps) {
  590.             --dir;    /* Back to last entry in the block */
  591.             break;    /* and move it no matter what */
  592.             }
  593.         }
  594.         CopyMem((byte *)dir, tosec + new->msfl_DirOffset,
  595.             (long) sizeof(struct MsDirEntry));
  596.         MarkSecDirty(tosec);
  597.         RootLock->msfl_DirSector = Disk.rootdir;
  598.         RootLock->msfl_DirOffset = (byte *) dir - fromsec;
  599.  
  600.         FreeSec(tosec);
  601.         FreeSec(fromsec);
  602.  
  603.         }
  604.     }
  605.     EmptyFileLock = NULL;
  606.     MSUnLock(new);
  607.     }
  608.     if (error == 0) {
  609.     /*
  610.      * The easy part: Copy the name to Disk.vollabel and RootLock.
  611.      */
  612.     {
  613.         int         i;
  614.         byte       *s,
  615.                *d;
  616.  
  617.         s = newname;
  618.         d = Disk.vollabel.de_Msd.msd_Name;
  619.         for (i = 0; i < L_8 + L_3; i++) {
  620. #if LONGNAMES
  621.         if (s[0])
  622.             *d++ = *s++;
  623.         else
  624.             *d++ = '\0';
  625. #else
  626.         if (s[0])
  627.             *d++ = ToUpper(*s++);
  628.         else
  629.             *d++ = ' ';
  630. #endif
  631.         }
  632.     }
  633.     RootLock->msfl_Msd = Disk.vollabel.de_Msd;    /* Just for the name and
  634.                              * dates */
  635.     WriteFileLock(RootLock);
  636.  
  637.     return DOSTRUE;
  638.     }
  639.     return DOSFALSE;
  640. #endif
  641. }
  642.  
  643. struct PrivateInfo *
  644. PrivateInfo()
  645. {
  646.     static struct PrivateInfo info = {
  647.     PRIVATE_REVISION,
  648.     sizeof(struct PrivateInfo),
  649.     RCSId,
  650.     &CheckBootBlock,
  651.     &DefaultConversion,
  652.     &DiskIOReq
  653. #if CONVERSIONS
  654.     ,2,    /* == ConvFence - 1 */
  655.     &Table_FromPC, &Table_ToPC,
  656.     &Table_FromST, &Table_ToST
  657. #endif
  658.     };
  659.  
  660.     return &info;
  661. }
  662.  
  663. #if HDEBUG
  664.  
  665. _abort()
  666. {
  667.     HanCloseDown();
  668.     RemTask(NULL);
  669. }
  670.  
  671. #endif                /* HDEBUG */
  672.